// balking.cpp


#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <ctime>

#include <chrono>
#include <fstream>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>


namespace blogs
{


using std::chrono::milliseconds;
using std::lock_guard;
using std::mutex;
using std::ofstream;
using std::ostringstream;
using std::string;


string
this_thread_id() {
    ostringstream oss;
    oss << std::this_thread::get_id();
    return oss.str();
}


class Data
{

  private:

    string
    __filename__;


    string
    __content__;


    bool
    __changed__;


    mutex
    __mtx__;


    void
    __do_save__()
    {
        std::printf("%s calls __do_save__, contents = %s\n", this_thread_id().c_str(), __content__.c_str());
        ofstream ofs(__filename__);
        ofs << __content__;
        ofs.close();
    }


  public:

    Data(string filename, string content)
        : __filename__(filename), __content__(content), __changed__(true)
    {
    }


    void
    change(string newcontent)
    {
        lock_guard<mutex> lk(__mtx__);
        __content__ = newcontent;
        __changed__ = true;
    }


    void
    save()
    {
        lock_guard<mutex> lk(__mtx__);
        if (!__changed__)
            return;
        __do_save__();
        __changed__ = false;
    }

};


class Saver
{

  private:

    Data &
    __data__;


  public:

    Saver(Data & data)
        : __data__(data)
    {
    }


    void
    run()
    {
        std::printf("Saver: %s\n", this_thread_id().c_str());
        while (true) {
            std::this_thread::sleep_for(milliseconds(1000));
            __data__.save();
        }
    }

};


class Changer
{

  private:

    Data &
    __data__;


  public:

    Changer(Data & data)
        : __data__(data)
    {
    }


    void
    run()
    {
        ostringstream id;
        id << std::this_thread::get_id();
        std::printf("Changer: %s\n", id.str().c_str());
        std::this_thread::sleep_for(milliseconds(100));
        for (int i = 0; true; ++i) {
            string s("No.");
            s += std::to_string(i);
            __data__.change(s);
            std::this_thread::sleep_for(milliseconds(std::rand()%1500));
            __data__.save();
        }
    }

};


void
bye(int sig = SIGTERM)
{
    std::printf("\nBye Balking...\n\n");
    std::exit(sig);
}


} // end of namespace blogs


int
main()
{
    using std::thread;
    using blogs::Changer;
    using blogs::Data;
    using blogs::Saver;

    std::signal(SIGINT, blogs::bye);
    std::srand(std::time(0));

    Data data("data.txt", "(empty)");
    Changer c(data);
    Saver   s(data);

    thread t1(&Changer::run, &c);
    thread t2(&Saver::run, &s);

    t1.join();
    t2.join();

    blogs::bye();
    return 0;
}

